pip install fastapi uvicorn sqlalchemy jinja2 python-multipart psycopg2-binary python-dotenv

БД:

create table punkts(
name varchar(255) primary key unique not null
);

create table users(
role varchar(50) not null,
name varchar(255) primary key not null,
login varchar(255) unique not null,
password varchar(10) not null
);

create table products(
articul varchar(6) primary key not null,
name varchar(255) not null,
quantity_name varchar(5) not null,
price integer not null,
dealer varchar(50) not null,
maker varchar(50) not null,
category varchar(50) not null,
discount integer not null,
quantity integer not null,
description varchar(255) not null,
picture varchar(10)
);


create table orders(
id serial primary key,
order_number integer not null,
articul varchar(6) not null,
count integer not null,
order_date date not null,
delivery_date date,
punkt_adres varchar(255) not null,
name varchar(255) not null,
code integer not null,
status varchar(50) not null,
foreign key(articul) references products(articul),
foreign key(punkt_adres) references punkts(name),
foreign key(name) references users(name)
);

.env

DB_PORT = 5432
DB_HOST = localhost
DB_NAME = airport
DB_PASSWORD = 123
DB_USER = postgres


detabase.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from dotenv import load_dotenv
import os

load_dotenv()

DB_PORT = os.getenv("DB_PORT")
DB_HOST = os.getenv("DB_HOST")
DB_NAME = os.getenv("DB_NAME")
DB_PASSWORD = os.getenv("DB_PASSWORD")
DB_USER = os.getenv("DB_USER")

DB_URL = f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"

engine = create_engine(DB_URL)
SessionLocal = sessionmaker(autocommit = False, autoflush = False, bind = engine)
Base = declarative_base()

def get_db():
    with SessionLocal() as session:
        yield session



models:

from sqlalchemy import Column, VARCHAR, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import Date
from database import Base

class Punkt(Base):
    __tablename__ = "punkts"

    name = Column(VARCHAR(255), primary_key = True,index=True)
    
    orders = relationship("Order", back_populates="punkts")


class User(Base):
    __tablename__ = "users"

    role = Column(VARCHAR(50), nullable=False)
    name = Column(VARCHAR(255), primary_key = True,index=True)
    login = Column(VARCHAR(255), unique=True, nullable=False)
    password = Column(VARCHAR(10), nullable=False)

    orders = relationship("Order", back_populates="users")


class Product(Base):
    __tablename__ = "products"

    articul = Column(VARCHAR(6), primary_key = True,index=True)
    name = Column(VARCHAR(255), nullable=False)
    quantity_name = Column(VARCHAR(5), nullable=False)
    price = Column(Integer, nullable=False)
    dealer  = Column(VARCHAR(50), nullable=False)
    maker  = Column(VARCHAR(50), nullable=False)
    category  = Column(VARCHAR(50), nullable=False)
    discount = Column(Integer,  nullable=False)
    quantity = Column(Integer, nullable=False)
    description = Column(VARCHAR(255), nullable=False)
    picture  = Column(VARCHAR(10))

    orders = relationship("Order", back_populates="products")


class Order(Base):
    __tablename__ = "orders"

    id = Column(Integer, primary_key = True,index=True)
    order_number = Column(Integer, nullable=False)
    articul = Column(VARCHAR(6), ForeignKey("products.articul"), nullable=False)
    count = Column(Integer, nullable=False)
    order_date = Column(Date, nullable=False)
    delivery_date = Column(Date)
    punkt_adres = Column(VARCHAR(255), ForeignKey("punkts.name"), nullable=False)
    name = Column(VARCHAR(255), ForeignKey("users.name"), nullable=False)
    code = Column(Integer, nullable=False)
    status = Column(VARCHAR(50), nullable=False)

    users = relationship("User", back_populates="orders")
    products = relationship("Product", back_populates="orders")
    punkts = relationship("Punkt", back_populates="orders")

main.py:

from fastapi import FastAPI, Depends, Request, status, Form
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session, joinedload
from database import get_db
import models
from datetime import datetime
from urllib.parse import quote, unquote

app = FastAPI()
templates = Jinja2Templates(directory="templates")
app.mount("/static", StaticFiles(directory="static"), name="static")

# Вспомогательная функция для безопасного чтения русских строк из Cookie
def get_user_from_cookies(request: Request):
    raw_role = request.cookies.get("user_role", "Гость")
    raw_name = request.cookies.get("user_name", "Гость")
    return unquote(raw_role), unquote(raw_name)

# Общая функция фильтрации и сортировки для Менеджера и Администратора
def apply_filters_and_sorting(query, search: str, sort_by: str, discount_filter: str):
    if search:
        search_expr = f"%{search}%"
        query = query.filter(
            models.Product.name.ilike(search_expr) |
            models.Product.category.ilike(search_expr) |
            models.Product.description.ilike(search_expr) |
            models.Product.maker.ilike(search_expr) |
            models.Product.dealer.ilike(search_expr) |
            models.Product.articul.ilike(search_expr)
        )
    
    if discount_filter == "0-12":
        query = query.filter(models.Product.discount >= 0, models.Product.discount < 12)
    elif discount_filter == "12-19":
        query = query.filter(models.Product.discount >= 12, models.Product.discount < 19)
    elif discount_filter == "19+":
        query = query.filter(models.Product.discount >= 19)
        
    if sort_by == "price_asc":
        query = query.order_by(models.Product.price.asc())
    elif sort_by == "price_desc":
        query = query.order_by(models.Product.price.desc())
    elif sort_by == "quantity_asc":
        query = query.order_by(models.Product.quantity.asc())
    elif sort_by == "quantity_desc":
        query = query.order_by(models.Product.quantity.desc())
        
    return query


# --- АВТОРИЗАЦИЯ ---

@app.get("/")
def login_page(request: Request):
    return templates.TemplateResponse(request, "login.html")

@app.post("/")
def process_login(
    request: Request,
    login: str = Form(...),
    password: str = Form(...),
    db: Session = Depends(get_db)
):
    user = db.query(models.User).filter(models.User.login == login).first()

    if not user or user.password != password:
        return templates.TemplateResponse(
            request, "login.html", {"error": "Неверный логин или пароль!"}
        )
    
    role_redirects = {
        "Авторизированный клиент": "/products/client",
        "Менеджер": "/products/manager",
        "Администратор": "/products/admin"
    }
    
    target_url = role_redirects.get(user.role, "/products")
    response = RedirectResponse(url=target_url, status_code=status.HTTP_303_SEE_OTHER)
    
    response.set_cookie(key="user_role", value=quote(user.role))
    response.set_cookie(key="user_name", value=quote(user.name))
    return response

@app.get("/logout")
def logout():
    response = RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER)
    response.delete_cookie("user_role")
    response.delete_cookie("user_name")
    return response


# --- ЭКРАНЫ ТОВАРОВ ---

@app.get("/products")
def get_products_general(request: Request, db: Session = Depends(get_db)):
    products = db.query(models.Product).all()
    return templates.TemplateResponse(request, "products_guest.html", {"products": products})

@app.get("/products/client")
def get_products_client(request: Request, db: Session = Depends(get_db)):
    user_role, user_name = get_user_from_cookies(request)
    products = db.query(models.Product).all()
    return templates.TemplateResponse(request, "products_client.html", {
        "products": products, "user_name": user_name, "user_role": user_role
    })

@app.get("/products/manager")
def get_products_manager(
    request: Request, search: str = None, sort_by: str = None, discount_filter: str = None, db: Session = Depends(get_db)
):
    user_role, user_name = get_user_from_cookies(request)
    query = db.query(models.Product)
    filtered_query = apply_filters_and_sorting(query, search, sort_by, discount_filter)
    return templates.TemplateResponse(request, "products_manager.html", {
        "products": filtered_query.all(), "user_name": user_name, "user_role": user_role,
        "search_query": search, "sort_by": sort_by, "discount_filter": discount_filter
    })

@app.get("/products/admin")
def get_products_admin(
    request: Request, search: str = None, sort_by: str = None, discount_filter: str = None, db: Session = Depends(get_db)
):
    user_role, user_name = get_user_from_cookies(request)
    query = db.query(models.Product)
    filtered_query = apply_filters_and_sorting(query, search, sort_by, discount_filter)
    return templates.TemplateResponse(request, "products_admin.html", {
        "products": filtered_query.all(), "user_name": user_name, "user_role": user_role,
        "search_query": search, "sort_by": sort_by, "discount_filter": discount_filter
    })


# --- МОДУЛЬ ЗАКАЗОВ

@app.get("/orders")
def get_orders_page(request: Request, db: Session = Depends(get_db)):
    user_role, user_name = get_user_from_cookies(request)
    if user_role not in ["Менеджер", "Администратор"]:
        return RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER)
        
    orders = db.query(models.Order).options(
        joinedload(models.Order.products),
        joinedload(models.Order.users)
    ).all()
    return templates.TemplateResponse(request, "orders.html", {
        "orders": orders, "user_name": user_name, "user_role": user_role
    })

@app.get("/orders/add")
def add_order_form(request: Request, db: Session = Depends(get_db)):
    user_role, _ = get_user_from_cookies(request)
    if user_role != "Администратор":
        return RedirectResponse(url="/orders", status_code=status.HTTP_303_SEE_OTHER)
    
    products = db.query(models.Product).all()
    punkts = db.query(models.Punkt).all()
    users = db.query(models.User).all()
    return templates.TemplateResponse(request, "order_form.html", {
        "action_url": "/orders/add", "title": "Добавление заказа",
        "products": products, "punkts": punkts, "users": users
    })

@app.post("/orders/add")
def process_add_order(
    request: Request, order_number: int = Form(...), articul: str = Form(...), count: int = Form(...),
    order_date: str = Form(...), delivery_date: str = Form(None), punkt_adres: str = Form(...),
    name: str = Form(...), code: int = Form(...), status_val: str = Form(..., alias="status"),
    db: Session = Depends(get_db) # УБРАНО ПОЛЕ description
):
    user_role, _ = get_user_from_cookies(request)
    if user_role != "Администратор":
        return RedirectResponse(url="/orders", status_code=status.HTTP_303_SEE_OTHER)

    new_order = models.Order(
        order_number=order_number, articul=articul, count=count,
        order_date=datetime.strptime(order_date, "%Y-%m-%d").date(),
        delivery_date=datetime.strptime(delivery_date, "%Y-%m-%d").date() if delivery_date else None,
        punkt_adres=punkt_adres, name=name, code=code, status=status_val
    )
    db.add(new_order)
    db.commit()
    return RedirectResponse(url="/orders", status_code=status.HTTP_303_SEE_OTHER)

@app.get("/orders/edit/{order_id}")
def edit_order_form(order_id: int, request: Request, db: Session = Depends(get_db)):
    user_role, _ = get_user_from_cookies(request)
    if user_role != "Администратор":
        return RedirectResponse(url="/orders", status_code=status.HTTP_303_SEE_OTHER)
        
    order = db.query(models.Order).filter(models.Order.id == order_id).first()
    products = db.query(models.Product).all()
    punkts = db.query(models.Punkt).all()
    users = db.query(models.User).all()
    return templates.TemplateResponse(request, "order_form.html", {
        "action_url": f"/orders/edit/{order_id}", "title": "Редактирование заказа", "order": order,
        "products": products, "punkts": punkts, "users": users
    })

@app.post("/orders/edit/{order_id}")
def process_edit_order(
    order_id: int, request: Request, order_number: int = Form(...), articul: str = Form(...), count: int = Form(...),
    order_date: str = Form(...), delivery_date: str = Form(None), punkt_adres: str = Form(...),
    name: str = Form(...), code: int = Form(...), status_val: str = Form(..., alias="status"),
    db: Session = Depends(get_db) # УБРАНО ПОЛЕ description
):
    user_role, _ = get_user_from_cookies(request)
    if user_role != "Администратор":
        return RedirectResponse(url="/orders", status_code=status.HTTP_303_SEE_OTHER)

    order = db.query(models.Order).filter(models.Order.id == order_id).first()
    if order:
        order.order_number = order_number
        order.articul = articul
        order.count = count
        order.order_date = datetime.strptime(order_date, "%Y-%m-%d").date()
        order.delivery_date = datetime.strptime(delivery_date, "%Y-%m-%d").date() if delivery_date else None
        order.punkt_adres = punkt_adres
        order.name = name
        order.code = code
        order.status = status_val
        db.commit()
    return RedirectResponse(url="/orders", status_code=status.HTTP_303_SEE_OTHER)

@app.post("/orders/delete/{order_id}")
def delete_order(order_id: int, request: Request, db: Session = Depends(get_db)):
    user_role, _ = get_user_from_cookies(request)
    if user_role != "Администратор":
        return RedirectResponse(url="/orders", status_code=status.HTTP_303_SEE_OTHER)
        
    order = db.query(models.Order).filter(models.Order.id == order_id).first()
    if order:
        db.delete(order)
        db.commit()
    return RedirectResponse(url="/orders", status_code=status.HTTP_303_SEE_OTHER)



order_form.html
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link rel="icon" href="/static/favicon.ico" type="image/x-icon">
    <style>
        body { font-family: Arial, padding: 30px; margin: 0; }
        .form-block {max-width: 400px;}
        .form-group {margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input, select { width: 100%; padding: 8px; box-sizing: border-box; font-family: Arial, sans-serif; }
        .btn-accent {padding: 10px 20px; font-weight: bold; cursor: pointer; font-family: Arial, sans-serif; }
        .btn-secondary {padding: 10px 20px; font-weight: bold; cursor: pointer; font-family: Arial, sans-serif; }
    </style>
</head>
<body>

    <h2>{{ title }}</h2>

    <div class="form-block">
        <form action="{{ action_url }}" method="POST">
            <div class="form-group">
                <label>Номер заказа:</label>
                <input type="number" name="order_number" value="{{ order.order_number if order else '' }}" required>
            </div>
            <div class="form-group">
                <label>Артикул:</label>
                <input type="text" name="articul" value="{{ order.articul if order else '' }}" required>
            </div>
            <div class="form-group">
                <label>Количество:</label>
                <input type="number" name="count" value="{{ order.count if order else 1 }}" required>
            </div>
            <div class="form-group">
                <label>Дата заказа:</label>
                <input type="date" name="order_date" value="{{ order.order_date if order else '' }}" required>
            </div>
            <div class="form-group">
                <label>Дата выдачи:</label>
                <input type="date" name="delivery_date" value="{{ order.delivery_date if order else '' }}">
            </div>
            <div class="form-group">
                <label>Адрес пункта выдачи:</label>
                <input type="text" name="punkt_adres" value="{{ order.punkt_adres if order else '' }}" required>
            </div>
            <div class="form-group">
                <label>ФИО Клиента:</label>
                <input type="text" name="name" value="{{ order.name if order else '' }}" required>
            </div>
            <div class="form-group">
                <label>Код получения (3 цифры):</label>
                <input type="number" name="code" value="{{ order.code if order else '' }}" required>
            </div>
            <div class="form-group">
                <label>Статус заказа:</label>
                <select name="status" required>
                    <option value="Новый" {% if order and order.status == 'Новый' %}selected{% endif %}>Новый</option>
                    <option value="В пути" {% if order and order.status == 'В пути' %}selected{% endif %}>В пути</option>
                    <option value="Завершен" {% if order and order.status == 'Завершен' %}selected{% endif %}>Завершен</option>
                </select>
            </div>

            <br>
            <button type="submit" class="btn-accent">Сохранить</button>
            <a href="/orders" class="btn-secondary">Отмена</a>
        </form>
    </div>

</body>
</html>


login.html

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Авторизация</title>
    <style>
        body { 
            font-family: Arial
        }
    </style>
</head>
<body>
    <img src="/static/icon.png" alt="Лого" class="header-logo" style = "width:5vw; high:5vw">
    <a class="guest-link" href="/products">Войти как гость</a>
    <h2>Авторизация</h2>     


    {% if error %}
        <p class="error-message">{{ error }}</p>
    {% endif %}

    <form action="/" method="POST">
        <div class="form-group">
            <input type="text" name="login" placeholder="Введите логин" required>
        </div>
        <div class="form-group">
            <input type="password" name="password" placeholder="Введите пароль" required>
        </div>
        <button type="submit">Войти</button>
    </form>


</body>
</html>



orders.html


<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Просмотр заказов</title>
    <link rel="icon" href="/static/favicon.ico" type="image/x-icon">
    <style>
        body { font-family: Arial; color: #000000}
        .header-panel { padding: 15px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; }
        .header-left { display: flex; align-items: center; gap: 15px; }
        .header-panel h1 { margin: 0; font-size: 20px; }
        .user-info { text-align: right; font-size: 14px; }

        .order-container { border: 2px solid #000000; padding: 15px; margin-bottom: 15px; display: flex; max-width: 800px; box-sizing: border-box; background-color: #FFFFFF; }
        .order-left-box { border: 1px solid #000000; padding: 10px; flex-grow: 1; display: flex; flex-direction: column; gap: 5px; }
        .order-right-box { border: 1px solid #000000; padding: 10px; width: 160px; min-width: 160px; margin-left: 15px; display: flex; align-items: center; justify-content: center; text-align: center; font-weight: bold; }
        
        .articul-title { font-weight: bold; }
        .actions { margin-top: 10px; display: flex; gap: 10px; }

    </style>
</head>
<body>

    <div class="header-panel">
        <div class="header-left">
            <img src="/static/icon.png" alt="Лого" class="header-logo" style = "width:5vw; high:5vw">
            <h1>Заказы</h1>
        </div>
        <div class="user-info">
            <strong>{{ user_name }}</strong> ({{ user_role }})<br><br>
            <a class="btn-secondary" href="/products/{{ 'admin' if user_role == 'Администратор' else 'manager' }}">Назад</a>
        </div>
    </div>

    {% if user_role == 'Администратор' %}
        <a class="btn-accent" href="/orders/add">Добавить заказ</a>
    {% endif %}

    {% for order in orders %}
    <div class="order-container">
        <div class="order-left-box">
            <div class="articul-title">Артикул заказа: {{ order.articul }}</div>
            <div>Статус заказа: {{ order.status }}</div>
            <div>Адрес пункта выдачи: {{ order.punkt_adres }}</div>
            <div>Дата заказа: {{ order.order_date }}</div>
            
            {% if user_role == 'Администратор' %}
            <div class="actions">
                <a class="btn-edit" href="/orders/edit/{{ order.id }}">Редактировать</a>
                <form action="/orders/delete/{{ order.id }}" method="POST" style="display:inline;" onsubmit="return confirm('Удалить этот заказ?')">
                    <button>Удалить</button>
                </form>
            </div>
            {% endif %}
        </div>
        <div class="order-right-box">
            <div>
                Дата доставки:<br>
                {{ order.delivery_date if order.delivery_date else 'Не указана' }}
            </div>
        </div>
    </div>
    {% else %}
        <p>Список заказов пуст.</p>
    {% endfor %}

</body>
</html>



products_admin.html

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Управление каталогом</title>
    <link rel="icon" href="/static/favicon.ico" type="image/x-icon">
    <style>
        input, select { font-family: Arial, sans-serif}
        body { font-family: Arial; color: #000000}
        .product-card { width: 100%; max-width: 1200px; border: 2px solid #000000; margin-bottom: 15px; border-collapse: collapse; }

        .cell-right { border: 1px solid #000000; width: 130px; text-align: center; font-size: 15px; }
        .cell-left { border: 1px solid #000000; width: 150px; height: 150px; text-align: center; vertical-align: middle; padding: 0; }
        .product-img { width: 150px; height: 150px; object-fit: cover; display: block; }
        
        .high-discount { background-color: #483D8B; color: #FFFFFF; }
        .out-of-stock { background-color: #808080; color: #FFFFFF; }
    </style>
</head>
<body>

    <table style="width: 100%; border-collapse: collapse;">
        <tr>
            <td style="vertical-align: middle; width: 60px;">
                <img src="/static/icon.png" alt="Лого" style="width: 50px; height: auto; display: block;">
            </td>
            <td style="vertical-align: middle;">
                <h1>Товары</h1>
            </td>
            <td style="text-align: right; vertical-align: middle;">
                <span><strong>{{ user_name }}</strong> ({{ user_role }})</span><br><br>
                <a href="/orders">Заказы</a>
                <a href="/logout">Выйти</a>
            </td>
        </tr>
    </table>

    <form action="/products/admin" style="margin-bottom: 20px">
        <input name="search" placeholder="Поиск" value="{{ search_query or '' }}" oninput="this.form.submit()">
        <select name="sort_by" onchange="this.form.submit()">
            <option value="">Без сортировки</option>
            <option value="price_asc" {% if sort_by == 'price_asc' %}selected{% endif %}>Цена (возр.)</option>
            <option value="price_desc" {% if sort_by == 'price_desc' %}selected{% endif %}>Цена (убыв.)</option>
        </select>
        <select name="discount_filter" onchange="this.form.submit()" style="margin-left: 5px;">
            <option value="">Все скидки</option>
            <option value="0-12" {% if discount_filter == '0-12' %}selected{% endif %}>0 - 11,99%</option>
            <option value="12-19" {% if discount_filter == '12-19' %}selected{% endif %}>12 - 18,99%</option>
            <option value="19+" {% if discount_filter == '19+' %}selected{% endif %}>19% и более</option>
        </select>
    </form>

    {% for prod in products %}
    <table class="product-card {% if prod.quantity == 0 %}out-of-stock{% elif prod.discount > 15 %}high-discount{% endif %}">
        <tr>
            <td class="cell-left">
                {% if prod.picture %}
                    <img src="/static/{{ prod.picture }}" class="product-img">
                {% else %}
                    <img src="/static/picture.png" class="product-img">
                {% endif %}
            </td>
            <td class="cell-info">
                <div>{{ prod.category }} | {{ prod.name }}</div>
                <div>Описание товара: {{ prod.description or 'Описание отсутствует' }}</div>
                <div>Производитель: {{ prod.maker }}</div>
                <div>Поставщик: {{ prod.dealer }}</div>
                <div>Цена: 
                    {% if prod.discount > 0 and prod.quantity > 0 %}
                        <span class="old-price" style="text-decoration: line-through; color: red;">{{ prod.price }} руб.</span>
                        <span>{{ ((prod.price * (1 - prod.discount / 100)) | float) | round(2) }} руб.</span>
                    {% else %}
                        <span>{{ prod.price }} руб.</span>
                    {% endif %}
                </div>
                <div>Единица измерения: {{ prod.quantity_name }}</div>
                <div>Количество на складе: {{ prod.quantity }}</div>
            </td>
            <td class="cell-right">
                Действующая скидка:<br><span style="font-size: 18px;">{{ prod.discount }}%</span>
            </td>
        </tr>
    </table>
    {% endfor %}

</body>
</html>



products_client.html



<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Авторизованный пользователь</title>
    <link rel="icon" href="/static/picture.png" type="image/x-icon">
    <style>
        body { font-family: Arial; color: #000000}
        .product-card { width: 100%; max-width: 1200px; border: 2px solid #000000; margin-bottom: 15px; border-collapse: collapse; }

        .cell-right { border: 1px solid #000000; width: 130px; text-align: center; font-size: 15px; }
        .cell-left { border: 1px solid #000000; width: 150px; height: 150px; text-align: center; vertical-align: middle; padding: 0; }
        .product-img { width: 150px; height: 150px; object-fit: cover; display: block; }
        
        .high-discount { background-color: #483D8B; color: #FFFFFF; }
        .out-of-stock { background-color: #808080; color: #FFFFFF; }
    </style>
</head>
<body>

    <table style="width: 100%; padding-bottom: 10px; margin-bottom: 15px; border-collapse: collapse;">
        <tr>
            <td style="vertical-align: middle; width: 60px;">
                <img src="/static/icon.png" style="width: 50px; height: auto; display: block;">
            </td>
            <td style="vertical-align: middle;">
                <h1 style="margin: 0; font-size: 24px;">Каталог велосипедов</h1>
            </td>
            <td style="text-align: right; vertical-align: middle;">
                <span><strong>{{ user_name }}</strong> (Клиент)</span><br><br>
                <a href="/logout" style="background-color: #4B0082; color: #FFFFFF; padding: 6px 12px; text-decoration: none; font-weight: bold; font-size: 14px; margin-left: 10px;">Выйти</a>
            </td>
        </tr>
    </table>


    {% for prod in products %}
    <table class="product-card {% if prod.quantity == 0 %}out-of-stock{% elif prod.discount > 15 %}high-discount{% endif %}">
        <tr>
            <td class="cell-left">
                {% if prod.picture %}
                    <img src="/static/{{ prod.picture }}" alt="photo" class="product-img">
                {% else %}
                    <img src="/static/picture.png" alt="no photo" class="product-img">
                {% endif %}
            </td>
            
            <td>
                <div style="font-weight: bold; font-size: 16px; margin-bottom: 5px;">{{ prod.category }} | {{ prod.name }}</div>
                <div style="margin-bottom: 4px;">Описание товара: {{ prod.description or 'Описание отсутствует' }}</div>
                <div style="margin-bottom: 4px;">Производитель: {{ prod.maker }}</div>
                <div style="margin-bottom: 4px;">Поставщик: {{ prod.dealer }}</div>
                <div style="margin-bottom: 4px;">Цена: 
                    {% if prod.discount > 0 and prod.quantity > 0 %}
                        <span style="text-decoration: line-through; color: red; margin-right: 5px;">{{ prod.price }} руб.</span>
                        <span style="font-weight: bold;">{{ ((prod.price * (1 - prod.discount / 100)) | float) | round(2) }} руб.</span>
                    {% else %}
                        <span style="font-weight: bold;">{{ prod.price }} руб.</span>
                    {% endif %}
                </div>
                <div style="margin-bottom: 4px;">Единица измерения: {{ prod.quantity_name }}</div>
                <div>Количество на складе: {{ prod.quantity }}</div>
            </td>
            
            <td class="cell-right">
                Действующая скидка:<br><span style="font-size: 18px;">{{ prod.discount }}%</span>
            </td>
        </tr>
    </table>
    {% else %}
        <p>Товары не найдены.</p>
    {% endfor %}

</body>
</html>


products_guest.html


<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Каталог товаров</title>
    <link rel="icon" href="/static/icon.png" type="image/x-icon">
    <style>
        body { font-family: Arial; color: #000000}
        .product-card { width: 100%; max-width: 1200px; border: 2px solid #000000; margin-bottom: 15px; border-collapse: collapse; }

        .cell-right { border: 1px solid #000000; width: 130px; text-align: center; font-size: 15px; }
        .cell-left { border: 1px solid #000000; width: 150px; height: 150px; text-align: center; vertical-align: middle; padding: 0; }
        .product-img { width: 150px; height: 150px; object-fit: cover; display: block; }
        
        .high-discount { background-color: #483D8B; color: #FFFFFF; }
        .out-of-stock { background-color: #808080; color: #FFFFFF; }
    </style>
</head>
<body>

    <table>
        <tr>
            <td style="vertical-align: middle; width: 60px;">
                <img src="/static/icon.ico" style="width: 50px; height: auto; display: block;">
            </td>
            <td style="vertical-align: middle;">
                <h1 style="margin: 0; font-size: 24px;">Каталог велосипедов</h1>
            </td>
            <td style="text-align: right; vertical-align: middle;">
                <a href="/" style="background-color: #4B0082; color: #FFFFFF; padding: 6px 12px; font-weight: bold; font-size: 14px;">Войти</a>
            </td>
        </tr>
    </table>

    {% for prod in products %}
    <table class="product-card {% if prod.quantity == 0 %}out-of-stock{% elif prod.discount > 15 %}high-discount{% endif %}">
        <tr>
            <td>
                {% if prod.picture %}
                    <img src="/static/{{ prod.picture }}" alt="photo" class="product-img">
                {% else %}
                    <img src="/static/picture.png" alt="no photo" class="product-img">
                {% endif %}
            </td>
            <td>
                <div style="font-weight: bold; font-size: 16px;">{{ prod.category }} | {{ prod.name }}</div>
                <div>Описание товара: {{ prod.description or 'Описание отсутствует' }}</div>
                <div>Производитель: {{ prod.maker }}</div>
                <div>Поставщик: {{ prod.dealer }}</div>
                <div>Цена: 
                    {% if prod.discount > 0 and prod.quantity > 0 %}
                        <span class="old-price" style="text-decoration: line-through; color: red; margin-right: 5px;">{{ prod.price }} руб.</span>
                        <span>{{ ((prod.price * (1 - prod.discount / 100)) | float) | round(2) }} руб.</span>
                    {% else %}
                        <span>{{ prod.price }} руб.</span>
                    {% endif %}
                </div>
                <div style="margin-bottom: 4px;">Единица измерения: {{ prod.quantity_name }}</div>
                <div>Количество на складе: {{ prod.quantity }}</div>
            </td>
            <td class="cell-right">
                Действующая скидка:<br><span style="font-size: 18px;">{{ prod.discount }}%</span>
            </td>
        </tr>
    </table>
    {% endfor %}

</body>
</html>



products_manager.html

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Панель Менеджера</title>
    <link rel="icon" href="/static/favicon.ico" type="image/x-icon">
    <style>
       input, select { font-family: Arial, sans-serif}
        body { font-family: Arial; color: #000000}
        .product-card { width: 100%; max-width: 1200px; border: 2px solid #000000; margin-bottom: 15px; border-collapse: collapse; }

        .cell-right { border: 1px solid #000000; width: 130px; text-align: center; font-size: 15px; }
        .cell-left { border: 1px solid #000000; width: 150px; height: 150px; text-align: center; vertical-align: middle; padding: 0; }
        .product-img { width: 150px; height: 150px; object-fit: cover; display: block; }
        
        .high-discount { background-color: #483D8B; color: #FFFFFF; }
        .out-of-stock { background-color: #808080; color: #FFFFFF; }
    </style>
</head>
<body>

    <table style="width: 100%; border-collapse: collapse;">
        <tr>
            <td style="vertical-align: middle; width: 60px;">
                <img src="/static/icon.png" alt="Лого" style="width: 50px; height: auto; display: block;">
            </td>
            <td style="vertical-align: middle;">
                <h1>Товары</h1>
            </td>
            <td style="text-align: right; vertical-align: middle;">
                <span><strong>{{ user_name }}</strong> ({{ user_role }})</span><br><br>
                <a href="/orders">Заказы</a>
                <a href="/logout">Выйти</a>
            </td>
        </tr>
    </table>

    <form action="/products/manager" style="margin-bottom: 20px">
       <input name="search" placeholder="Поиск" value="{{ search_query or '' }}" oninput="this.form.submit()">
        <select name="sort_by" onchange="this.form.submit()">
            <option value="">Без сортировки</option>
            <option value="price_asc" {% if sort_by == 'price_asc' %}selected{% endif %}>Цена (возр.)</option>
            <option value="price_desc" {% if sort_by == 'price_desc' %}selected{% endif %}>Цена (убыв.)</option>
        </select>
        <select name="discount_filter" onchange="this.form.submit()" style="margin-left: 5px;">
            <option value="">Все скидки</option>
            <option value="0-12" {% if discount_filter == '0-12' %}selected{% endif %}>0 - 11,99%</option>
            <option value="12-19" {% if discount_filter == '12-19' %}selected{% endif %}>12 - 18,99%</option>
            <option value="19+" {% if discount_filter == '19+' %}selected{% endif %}>19% и более</option>
        </select>
    </form>

    {% for prod in products %}
    <table class="product-card {% if prod.quantity == 0 %}out-of-stock{% elif prod.discount > 15 %}high-discount{% endif %}">
        <tr>
            <td class="cell-left">
                {% if prod.picture %}
                    <img src="/static/{{ prod.picture }}" class="product-img">
                {% else %}
                    <img src="/static/picture.png" class="product-img">
                {% endif %}
            </td>
            <td class="cell-info">
                <div>{{ prod.category }} | {{ prod.name }}</div>
                <div>Описание товара: {{ prod.description or 'Описание отсутствует' }}</div>
                <div>Производитель: {{ prod.maker }}</div>
                <div>Поставщик: {{ prod.dealer }}</div>
                <div>Цена: 
                    {% if prod.discount > 0 and prod.quantity > 0 %}
                        <span class="old-price" style="text-decoration: line-through; color: red;">{{ prod.price }} руб.</span>
                        <span>{{ ((prod.price * (1 - prod.discount / 100)) | float) | round(2) }} руб.</span>
                    {% else %}
                        <span>{{ prod.price }} руб.</span>
                    {% endif %}
                </div>
                <div>Единица измерения: {{ prod.quantity_name }}</div>
                <div>Количество на складе: {{ prod.quantity }}</div>
            </td>
            <td class="cell-right">
                Действующая скидка:<br><span style="font-size: 18px;">{{ prod.discount }}%</span>
            </td>
        </tr>
    </table>

    {% endfor %}

</body>
</html>
